{%- import "method-macro.cc.j2" as method_macro %}

{%- set class_name = "%sTestConsumer"|format(interface.mojom_name) %}

std::unique_ptr<{{class_name}}> {{class_name}}::Create(
    ::ash::cros_healthd::connectivity::Context* context) {
  return std::unique_ptr<{{class_name}}>(new {{class_name}}(context));
}

{{class_name}}::{{class_name}}(
    ::ash::cros_healthd::connectivity::Context* context)
  : context_(context) {
{%- for method in interface.methods %}
  {{method_macro.define_data_generator(
      method.mojom_name ~ "__", "context", method.parameters)}}
{%- endfor %}
}

void {{class_name}}::Bind(::mojo::PendingRemote<{{
    interface.mojom_name}}> remote) {
  remote_.reset();
  remote_.Bind(std::move(remote));
}

::mojo::PendingReceiver<{{interface.mojom_name}}> {{class_name}}::Generate() {
  has_next_ = false;
  remote_.reset();
  return remote_.BindNewPipeAndPassReceiver();
}

base::OnceCallback<void({{class_name}}::CheckCallback)>
{{class_name}}::CheckClosure() {
  return base::BindOnce(&{{class_name}}::Check, weak_factory_.GetWeakPtr());
}

{%  for method in interface.methods %}
{#- Step1: Call remote method and wait for it finished. #}
void {{class_name}}::CheckStep1__{{method.mojom_name}}(CheckCallback callback) {
  auto next_callback =
      base::BindOnce(&{{class_name}}::CheckStep2__{{method.mojom_name}},
                     weak_factory_.GetWeakPtr(),
                     std::move(callback));

{%-   if method.response_parameters == None %}
  context_->remote_state()->WaitLastCall(std::move(next_callback));
  CHECK(remote_.is_connected());
  remote_.set_disconnect_handler(
    context_->remote_state()->GetFulfillLastCallCallbackClosure());
  remote_->{{method.mojom_name}}({{method_macro.generate_params(
      method.mojom_name ~ "__", method.parameters)}});
{%-   else %}
  auto [next_callback1, next_callback2] =
    base::SplitOnceCallback(std::move(next_callback));
  CHECK(remote_.is_connected());
  remote_.set_disconnect_handler(std::move(next_callback1));
  auto response_callback = base::BindOnce([]({{
    method_macro.declare_params("", method.response_parameters)}}
  ){
{%-      for param in method.response_parameters %}
{%-        if param.kind is PendingReceiverKind or
              param.kind is PendingRemoteKind  %}
    CHECK(false) <<
        "Checking interface through response parameters are not supported.";
{%-        endif %}
{%-      endfor %}
    LOG(ERROR) << "{{method.mojom_name}} callback!";
  }).Then(std::move(next_callback2));

  remote_->{{method.mojom_name}}({{method_macro.generate_params(
    method.mojom_name ~ "__", method.parameters)}}
    {%- if method.parameters %}, {% endif -%}
    std::move(response_callback)
  );
{%-   endif %}
}

{#  Step2: Checks sub-interfaces. #}
void {{class_name}}::CheckStep2__{{method.mojom_name}}(CheckCallback callback) {
{#- If the remote was disconnected, connection error had occurred. #}
  if (!remote_.is_connected()) {
    LOG(ERROR) << "Check failed because connection error occurred. Failed on: "
               <<"{{interface.mojom_name}}::{{method.mojom_name}}.";
    std::move(callback).Run(false);
    return;
  }

  auto callback_0 = base::BindOnce(
    &{{class_name}}::CheckStep3__{{method.mojom_name}},
    weak_factory_.GetWeakPtr());

{#  Do the checking of TestConsumer. The next action will be run only if the #}
{#- previous one succeeded. #}
{%    for param in method.parameters | reverse | selectattr(
                                            "kind", "PendingReceiverKind")  %}
  auto callback_{{loop.index}} = base::BindOnce(
    &::ash::cros_healthd::connectivity::RunOrReturn,
    /*return_value=*/false,
    /*get_result=*/{{
      method.mojom_name}}__{{param.mojom_name}}__generator__->CheckClosure(),
    /*run_callback=*/std::move(callback_{{loop.index - 1}}));
{%-     if loop.last %}
  auto callback_last = std::move(callback_{{loop.index}});
{%-     endif %}
{%-   else %}
  auto callback_last = std::move(callback_0);
{%-   endfor %}
  std::move(callback_last).Run(std::move(callback));
}

{#  Step3: Checks if need to do another check. If any parameter or
    response_parameter need to be checked, do the check again. #}
void {{class_name}}::CheckStep3__{{method.mojom_name}}(CheckCallback callback) {
{%-   if method.parameters %}
  if ({{method_macro.params_has_next(
        method.mojom_name ~"__", method.parameters)}}) {
    CheckStep1__{{method.mojom_name}}(std::move(callback));
    return;
  }
{%-   endif %}
{%-   if method.response_parameters == None %}
  std::move(callback).Run(true);
{%-   else %}
  ::ash::cros_healthd::connectivity::RunOrReturn(
    /*return_value=*/true,
    /*get_result=*/context_->remote_state()->GetLastCallHasNextClosure(),
    /*run_callback=*/base::BindOnce(
      &{{class_name}}::CheckStep1__{{method.mojom_name}},
      weak_factory_.GetWeakPtr()),
    std::move(callback));
{%-   endif %}
}
{%  endfor %}

void {{class_name}}::SetCheckResult(CheckCallback callback, bool res) {
  check_reentry_status_ = false;
  check_result_ = res;
  std::move(callback).Run(res);
}

void {{class_name}}::Check(CheckCallback callback) {
  if (check_result_.has_value()) {
    std::move(callback).Run(check_result_.value());
    return;
  }

  CHECK(!check_reentry_status_)
    << "Check is call before the last run finished.";
  check_reentry_status_ = true;
  auto callback_0 = base::BindOnce(
      [](CheckCallback callback){ std::move(callback).Run(true); });

{#  Check each method of this interface. The next action will be run only if
    the previous one succeeded. #}
{%- for method in interface.methods | reverse %}
  auto callback_{{loop.index}} = base::BindOnce(
    &::ash::cros_healthd::connectivity::RunOrReturn,
    /*return_value=*/false,
    /*get_result=*/base::BindOnce(
      &{{class_name}}::CheckStep1__{{method.mojom_name}},
      weak_factory_.GetWeakPtr()),
    /*run_callback=*/std::move(callback_{{loop.index - 1}}));
{%-   if loop.last %}
  auto callback_last = std::move(callback_{{loop.index}});
{%-   endif %}
{%- else %}
  auto callback_last = std::move(callback_0);
{%  endfor %}
  std::move(callback_last).Run(base::BindOnce(
      &{{class_name}}::SetCheckResult, weak_factory_.GetWeakPtr(),
      std::move(callback)));
}
